using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace TcKs.MSOffice.WrapperAssembliesCodeGenerator {
	/// <summary>
	/// Generates source code of .NET wrapper for specified assembly/type/etc...
	/// </summary>
	public class CodeGenerator {
		private CodeGeneratorOptions options = new CodeGeneratorOptions();
		/// <summary>
		/// Options of code generation.
		/// </summary>
		public CodeGeneratorOptions Options {
			get { return this.options; }
		}

		private CodeGeneratorContext context = new CodeGeneratorContext();
		/// <summary>
		/// Context of code generation.
		/// </summary>
		public CodeGeneratorContext Context {
			get { return this.context; }
		}

		protected virtual string GetTranslatedTypeFullName( Type type ) {
			bool none;
			return this.GetTranslatedTypeFullName( type, out none );
		}
		/// <summary>
		/// Returns correct type name by original type.
		/// </summary>
		/// <param name="type">Original type.</param>
		/// <param name="isCustomType">Returns true, if the type is custom.</param>
		/// <returns></returns>
		protected virtual string GetTranslatedTypeFullName( Type type, out bool isCustomType ) {
			if ( null == type ) { throw new ArgumentNullException( "type" ); }

			Type tmpType = type;
			if ( type.Name.EndsWith( "&" ) ) {
				string strFullName = type.FullName.Substring( 0, type.FullName.Length - 1 );
				foreach ( Type tp in this.Context.TypeMap.Keys ) {
					if ( tp.FullName == strFullName ) {
						tmpType = tp;
						break;
					}
				}
			}

			string ret;
			GeneratedTypeInfo gti;
			this.Context.TypeMap.TryGetValue( tmpType, out gti );
			if ( !(isCustomType = !(null == gti)) ) { ret = tmpType.FullName; }
			else { ret = gti.GetFullName(); }

			if ( isCustomType && tmpType.IsEnum ) {
				isCustomType = false;
			}

			if ( ret.EndsWith( "&" ) ) { ret = ret.Substring( 0, ret.Length - 1 ); }
			if ( ret.Contains( "[]" ) ) { ret = ret.Replace( "[]", "()" ); }

			ret = "Global." + ret;
			return ret;
		}
		/// <summary>
		/// Returns TRUE if specified type is abstract.
		/// </summary>
		/// <param name="type">Checked type.</param>
		/// <returns></returns>
		protected virtual bool IsAbstractClass( Type type ) {
			bool ret = false;
			TypeSpecification ts;
			this.Context.TypeSpecifications.TryGetValue( type, out ts );
			if ( null != ts ) {
				ret = ts.IsAbstractClass;
			}

			return ret;
		}
		/// <summary>
		/// Returns TRUE if specified type is "Application" type.
		/// </summary>
		/// <param name="type">Checked type.</param>
		/// <returns></returns>
		protected virtual bool IsApplicationType( Type type ) {
			bool ret = this.Context.ApplicationTypes.Contains( type );
			if ( false == ret ) {
				TypeSpecification ts;
				this.Context.TypeSpecifications.TryGetValue( type, out ts );
				if ( null != ts && null != ts.ForcedBaseType ) {
					ret = this.IsApplicationType( ts.ForcedBaseType );
				}
			}
			return ret;
		}

		/// <summary>
		/// Generates source code of .NET wrapper by specified interop assembly.
		/// </summary>
		/// <param name="modelAssembly">Interop assembly.</param>
		/// <param name="code">Source code generator for VB.NET language.</param>
		public virtual void GenerateCodeForAssembly( Assembly modelAssembly, VbNetSourceCodeGenerator code ) {
			if ( null == modelAssembly ) { throw new ArgumentNullException( "modelAssembly" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			Type[] arr_ModelTypes = modelAssembly.GetTypes();
			foreach ( Type tpModel in arr_ModelTypes ) {
				if ( false == tpModel.IsPublic ) { continue; }
				if ( this.Options.IgnoredTypes.Contains( tpModel ) ) { continue; }

				GenerateCodeForType( tpModel, code );
			}
		}

		public virtual void GenerateCodeForType( Type modelType, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }

			bool isClass = false;
			if ( modelType.IsClass ) { isClass = true; }
			else if ( this.Options.GenerateInterfaceAsClass && modelType.IsInterface ) { isClass = true; }

			if ( isClass ) {
				this.GenerateCodeForType_Class( modelType, code );
			}
			else if ( modelType.IsInterface ) {
				this.GenerateCodeForType_Interface( modelType, code );
			}
			else if ( modelType.IsEnum ) {
				this.GenerateCodeForType_Enum( modelType, code );
			}
			else if ( modelType.IsValueType ) {
				this.GenerateCodeForType_Struct( modelType, code );
			}
			else if ( modelType.IsSubclassOf( typeof( Delegate ) ) ) {
				this.GenerateCodeForType_Delegate( modelType, code );
			}
			else {
				if ( false == this.Options.IgnoreNotSupportedTypes ) {
					string msg = string.Format( "Can not generate source code from model type '{0}' because is not a class/interface/struct or enum.", modelType.AssemblyQualifiedName );
					throw new NotSupportedException( msg );
				}
			}
		}

		public virtual void GenerateCodeForType_Class( Type modelType, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			bool isAbstractClass;
			code.Public.zZz();
			if ( isAbstractClass = this.IsAbstractClass( modelType ) ) { code.MustInherit.zZz(); }
			code.Class._( modelType.Name ).Line();
			using ( code.IndentBlock() ) {
				string strBaseTypeFullName = this.GetBaseTypeFullNameOrThrow( modelType );
				code.Inherits_( strBaseTypeFullName ).Line();
				code.Line();

				code.Friend.Sub.New._V.ByVal._( "nativeObject" )._As_<object>();

				bool isApplicationType;
				if ( false == (isApplicationType = this.IsApplicationType( modelType )) ) {
					code.i.__.ByVal._( "rootObject" )._As_<TcKs.MSOffice.Common.RootObjectWrapperBase>();
				}
				code.V_.Line();

				using ( code.IndentBlock() ) {
					code.MyBase.o.New._V._( "nativeObject" );
					if ( false == isApplicationType ) {
						code.i_( "rootObject" );
					}
					code.V_.Line();
				}
				code.End.Sub.Line();
				code.Line();

				TypeSpecification ts;
				this.Context.TypeSpecifications.TryGetValue( modelType, out ts );
				if ( null != ts ) {
					if ( ts.IsCollection ) {
						this.GenerateCodeForTypeMethod__CollectionWrapperBase_WrappItem( modelType, ts, code );
					}
					if ( false == string.IsNullOrEmpty( ts.ApplicationCOMName ) ) {
						this.GenerateCodeForTypeMethod__CreateApplication( modelType, ts, code );
					}
				}

				this.GenerateCodeForTypeMembers( modelType, code );
			}
			code.End.Class.Line();
		}
		public virtual void GenerateCodeForType_Interface( Type modelType, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			code.Public.Interface._( modelType.Name ).Line();
			using ( code.IndentBlock() ) {
				MemberInfo[] arr_ModelMembers = modelType.GetMembers( BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
				foreach ( MemberInfo modelMember in arr_ModelMembers ) {
					this.GenerateCodeForInterfaceMember( modelMember, code );
				}
			}
			code.End.Interface.Line();
		}
		public virtual void GenerateCodeForType_Enum( Type modelType, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			code.Public.Enum._( modelType.Name ).Line();
			using ( code.IndentBlock() ) {
				FieldInfo[] arr_Fields = modelType.GetFields( BindingFlags.Static | BindingFlags.Public );
				for ( int i = 0; i < arr_Fields.Length; i++ ) {
					FieldInfo fi = arr_Fields[i];
					object defaultValue = fi.GetValue( null );
					string strDefaultValue = string.Format( "{0}", (int)defaultValue );
					code._( fi.Name )._E_( strDefaultValue ).Line();
				}
			}
			code.End.Enum.Line();
		}
		public virtual void GenerateCodeForType_Struct( Type modelType, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			code.Public.Structure._( modelType.Name ).Line();
			using ( code.IndentBlock() ) {
				this.GenerateCodeForTypeMembers( modelType, code );
			}
			code.End.Structure.zZz();
		}
		public virtual void GenerateCodeForType_Delegate( Type modelType, VbNetSourceCodeGenerator code ) {
			throw new NotSupportedException();
		}

		public virtual void GenerateCodeForInterfaceMember( MemberInfo modelMember, VbNetSourceCodeGenerator code ) {
			if ( null == modelMember ) { throw new ArgumentNullException( "modelMember" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			throw new NotImplementedException();
		}
		public virtual void GenerateCodeForTypeMembers( Type modelType, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			TypeSpecification tsModelType;
			this.Context.TypeSpecifications.TryGetValue( modelType, out tsModelType );

			MemberInfo[] arr_ModelMembers = modelType.GetMembers( BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
			foreach ( MemberInfo modelMember in arr_ModelMembers ) {
				if ( false == object.ReferenceEquals( modelMember.DeclaringType, modelType ) ) { continue; }
				if ( this.Options.NamesOfIgnoredTypeMembers.Contains( modelMember.Name ) ) { continue; }

				if ( null != tsModelType ) {
					if ( tsModelType.IgnoredMembers.Contains( modelMember ) ) { continue; }
				}

				MethodInfo tmpMI = modelMember as MethodInfo;
				if ( null != tmpMI ) {
					if ( tmpMI.Name.StartsWith( "get_" ) || tmpMI.Name.StartsWith( "set_" ) ) {
						string tmpName = tmpMI.Name.Substring( "get_".Length );
						PropertyInfo tmpPI = modelType.GetProperty( tmpName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
						if ( null != tmpPI ) { continue; }
					}
					else if ( tmpMI.Name.StartsWith( "add_" ) ) {
						string tmpName = tmpMI.Name.Substring( "add_".Length );
						EventInfo tmpEI = modelType.GetEvent( tmpName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
						if ( null != tmpEI ) { continue; }
					}
					else if ( tmpMI.Name.StartsWith( "remove_" ) ) {
						string tmpName = tmpMI.Name.Substring( "remove_".Length );
						EventInfo tmpEI = modelType.GetEvent( tmpName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
						if ( null != tmpEI ) { continue; }
					}
					else if ( tmpMI.Name.StartsWith( "raise_" ) ) {
						string tmpName = tmpMI.Name.Substring( "raise_".Length );
						EventInfo tmpEI = modelType.GetEvent( tmpName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
						if ( null != tmpEI ) { continue; }
					}
				}

				bool wasGeneratored = this.GenerateCodeForTypeMember( modelMember, code );
				if ( wasGeneratored ) {
					code.Line();
				}
			}
		}
		public virtual bool GenerateCodeForTypeMember( MemberInfo modelMember, VbNetSourceCodeGenerator code ) {
			if ( null == modelMember ) { throw new ArgumentNullException( "modelMember" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			bool ret = false;
			if ( modelMember is FieldInfo ) {
				ret = this.GenerateCodeForTypeField( ((FieldInfo)modelMember), code );
			}
			else if ( modelMember is PropertyInfo ) {
				ret = this.GenerateCodeForTypeProperty( (PropertyInfo)modelMember, code );
			}
			else if ( modelMember is MethodInfo ) {
				ret = this.GenerateCodeForTypeMethod( (MethodInfo)modelMember, code );
			}
			else if ( modelMember is EventInfo ) {
				if ( false == this.Options.IgnoreNotSupportedMembers ) {
					throw new NotImplementedException();
				}
			}
			else {
				if ( false == this.Options.IgnoreNotSupportedMembers ) {
					string messge = "Can not generate member from model member '{0}.{1}', because this type of member is not supported.";
					messge = string.Format( messge, modelMember.DeclaringType.FullName, modelMember.Name );
					throw new NotSupportedException( messge );
				}
			}

			return ret;
		}

		public virtual bool GenerateCodeForTypeField( FieldInfo modelField, VbNetSourceCodeGenerator code ) {
			if ( null == modelField ) { throw new ArgumentNullException( "modelField" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			if ( this.Options.IgnoredTypes.Contains( modelField.FieldType ) ) { return false; }

			string strTypeFullName = this.GetTranslatedTypeFullName( modelField.FieldType );
			code.Public._( modelField.Name )._As_( strTypeFullName ).Line();

			return true;
		}
		public virtual bool GenerateCodeForTypeProperty( PropertyInfo modelProperty, VbNetSourceCodeGenerator code ) {
			if ( null == modelProperty ) { throw new ArgumentNullException( "modelProperty" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }

			if ( this.Options.IgnoredTypes.Contains( modelProperty.PropertyType ) ) { return false; }

			ParameterInfo[] arr_Parameters = null;
			if ( modelProperty.CanRead ) {
				arr_Parameters = modelProperty.GetGetMethod( true ).GetParameters();
			}

			bool isReadOnly = !modelProperty.CanWrite;
			if ( modelProperty.CanWrite && false == modelProperty.CanRead ) {
				string message = "Property '{0}.{1}' can not be generated, because write-only properties is not supported";
				message = string.Format( message, modelProperty.DeclaringType.FullName, modelProperty.Name );
				
				//throw new NotSupportedException( message );
				return false;
			}
			else if ( false == modelProperty.CanRead && false == modelProperty.CanWrite ) {
				string message = "Property '{0}.{1}' can not be generated, because property has no getter and setter.";
				message = string.Format( message, modelProperty.DeclaringType.FullName, modelProperty.Name );
				
				//throw new NotSupportedException( message );
				return false;
			}

			if ( null == arr_Parameters || arr_Parameters.Length == 0 ) {
				code.Public._IF( isReadOnly, "ReadOnly " ).Property.zZz();

				bool isCustomType;
				string strTypeName = this.GetTranslatedTypeFullName( modelProperty.PropertyType, out isCustomType );
				code._( modelProperty.Name ).__.As_( strTypeName ).Line();

				if ( modelProperty.CanRead ) {
					using ( code.IndentBlock() ) {
						code.Get.Line();
						using ( code.IndentBlock() ) {
							bool isApplicationType = this.IsApplicationType( modelProperty.PropertyType );
							if ( isApplicationType ) {
								//code.Dim_( "ret" )._As_( strTypeName )._E.__.Me.o_( "FindTopRootObject()" ).Line();
								code.Dim_( "ret" )._As_( strTypeName )._E.__.Me.zZz();
								if ( false == this.IsApplicationType( modelProperty.DeclaringType ) ) {
									code.o_( "FindTopRootObject()" );
								}
								code.Line();
								code.Return_( "ret" ).Line();
							}
							else if ( isCustomType && false == modelProperty.PropertyType.IsEnum ) {
								code.Dim_( "nativeObject" )._As_<object>()._E.MyBase.o_( "WrappedObject" ).o_( modelProperty.Name ).Line();
								//code.Dim_( "ret" )._As.New_( strTypeName )._V._( "nativeObject" ).i.Me.o_( "RootObject" ).V_.Line();
								Type tpOwner = modelProperty.DeclaringType;
								bool isAppType = this.IsApplicationType( tpOwner );
								code.Dim_( "ret" )._As.New_( strTypeName )._V._( "nativeObject" ).i_( isAppType ? " Me" : "Me.RootObject" ).V_.Line();
								code.Return_( "ret" ).Line();
							}
							else {
								code.Return.MyBase.o_( "WrappedObject" ).o._( modelProperty.Name ).Line();
							}
						}
						code.End.Get.Line();
					}
				}
				if ( modelProperty.CanWrite ) {
					using ( code.IndentBlock() ) {
						code.Set._V.ByVal.Value.As_( strTypeName ).V_.Line();
						using ( code.IndentBlock() ) {
							if ( isCustomType && false == modelProperty.PropertyType.IsEnum ) {
								code.Dim_( "nativeObject" )._As_<object>()._E.Nothing.Line();
								code.If_( "Value" ).__.IsNotNothing.Then._( "nativeObject" )._E.Value.o_( "GetWrappedObject()" ).Line();
								code.MyBase.o_( "WrappedObject" ).o_( modelProperty.Name )._E_( "nativeObject" ).Line();
							}
							else {
								code.MyBase.o_( "WrappedObject" ).o_( modelProperty.Name )._E.Value.Line();
							}
						}
						code.End.Set.Line();
					}
				}

				code.End.Property.Line();

				return true;
			}
			else {
				if ( false == this.Options.IgnorePropertiesWithParameters ) {
					// TODO: add overloading for methods with optional parameters
					MethodInfo modelMethod_Get = modelProperty.GetGetMethod();
					MethodInfo modelMethod_Set = modelProperty.GetSetMethod();

					bool okGet = false;
					bool okSet = false;
					if ( null != modelMethod_Get ) {
						string strCustomName = "Get" + modelProperty.Name;
						okGet = this.GenerateCodeForTypeMethod( modelMethod_Get, code, strCustomName );
					}
					if ( null != modelMethod_Set ) {
						string strCustomName = "Set" + modelMethod_Set.Name;
						okSet = this.GenerateCodeForTypeMethod( modelMethod_Set, code, strCustomName );
					}

					return okGet || okSet;
					//string message = "Property '{0}.{1}' can not be generated, because has parameters.";
					//message = string.Format( message, modelProperty.DeclaringType.FullName, modelProperty.Name );
					//throw new NotSupportedException( message );
				}
			}

			return false;
		}
		public virtual bool GenerateCodeForTypeMethod( MethodInfo modelMethod, VbNetSourceCodeGenerator code ) {
			return this.GenerateCodeForTypeMethod( modelMethod, code, (string)null );
		}
		public virtual bool GenerateCodeForTypeMethod( MethodInfo modelMethod, VbNetSourceCodeGenerator code, string customName ) {
			return this.GenerateCodeForTypeMethod( modelMethod, code, customName, (ParameterInfo[])null );
		}
		public virtual bool GenerateCodeForTypeMethod( MethodInfo modelMethod, VbNetSourceCodeGenerator code, string customName, ParameterInfo[] arr_CustomParameters ) {
			if ( null == modelMethod ) { throw new ArgumentNullException( "modelMethod" ); }
			if ( this.Options.IgnoredTypes.Contains( modelMethod.ReturnType ) ) { return false; }

			string strMethodName = customName;
			if ( null != strMethodName ) { strMethodName = strMethodName.Trim(); }
			if ( string.IsNullOrEmpty( strMethodName ) ) { strMethodName = modelMethod.Name; }

			ParameterInfo[] arr_Parameters = arr_CustomParameters;
			if ( null == arr_Parameters ) { arr_Parameters = modelMethod.GetParameters(); }

			Type returnType = modelMethod.ReturnType;
			bool isCustomReturnType = false;
			string strReturnTypeName = null;
			if ( null != returnType ) { strReturnTypeName = GetTranslatedTypeFullName( returnType, out isCustomReturnType ); }
			if ( strReturnTypeName == "Global.System.Void" ) { strReturnTypeName = string.Empty; }

			bool isFunction = !string.IsNullOrEmpty( strReturnTypeName );

			Dictionary<ParameterInfo, string> dic_ParameterMap = new Dictionary<ParameterInfo, string>();
			if ( null != arr_Parameters ) {
				foreach ( ParameterInfo pri in arr_Parameters ) {
					bool isParameterCustomType;
					string strParameterTypeName = this.GetTranslatedTypeFullName( pri.ParameterType, out isParameterCustomType );
					//if ( strParameterTypeName == "Microsoft.Office.Core.MsoDocInspectorStatus" ) {
					//    int a = 0;
					//}

					if ( isParameterCustomType || pri.ParameterType == typeof( object ) ) {
						dic_ParameterMap[pri] = "tmp_" + pri.Name;
					}
					else {
						dic_ParameterMap[pri] = "a" + pri.Name;
					}
				}
			}

			code.Public.zZz();
			if ( isFunction ) { code.Function.zZz(); } else { code.Sub.zZz(); }
			code._( strMethodName )._V.zZz();

			bool isFirstParameter = true;
			foreach ( KeyValuePair<ParameterInfo, string> kp in dic_ParameterMap ) {
				if ( isFirstParameter ) { isFirstParameter = false; } else { code.i.zZz(); }
				string strParameterTypeName = this.GetTranslatedTypeFullName( kp.Key.ParameterType );
				code._( "a" + kp.Key.Name ).__.As_( strParameterTypeName );
			}

			code.V_.zZz();
			if ( isFunction ) { code.__.As_( strReturnTypeName ); }
			code.Line();
			using ( code.IndentBlock() ) {
				foreach ( KeyValuePair<ParameterInfo, string> kp in dic_ParameterMap ) {
					ParameterInfo pri = kp.Key;
					bool isParameterCustomType;
					string strParameterTypeName = this.GetTranslatedTypeFullName( pri.ParameterType, out isParameterCustomType );
					if ( isParameterCustomType || pri.ParameterType == typeof( object ) ) {
						code.Dim_( kp.Value ).__.As_<object>()._E_<TcKs.MSOffice.Common.WrapperHelper>().o_( "GetWrappedObjectIfCan" )._V._( "a" + pri.Name ).V_.Line();
					}
				}

				if ( isFunction ) {
					code.Dim_( "result" )._As_<object>()._E.zZz();
				}

				code.MyBase.o_( "WrappedObject" ).o_( modelMethod.Name )._V.zZz();
				isFirstParameter = true;
				foreach ( KeyValuePair<ParameterInfo, string> kp in dic_ParameterMap ) {
					if ( isFirstParameter ) { isFirstParameter = false; } else { code._( ", " ); }
					code._( kp.Value );
				}
				code.V_.Line();

				if ( isFunction ) {
					if ( isCustomReturnType ) {
						Type tpOwner = modelMethod.DeclaringType;
						bool isAppType = this.IsApplicationType( tpOwner );
						code.Dim_( "ret" )._As.New_( strReturnTypeName )._V._( "result" ).i_( isAppType ? " Me" : "Me.RootObject" ).V_.Line();
						code.Return_( "ret" ).Line();
					}
					else {
						code.Return_( "result" ).Line();
					}
				}
			}
			code.End.zZz();
			if ( isFunction ) { code.Function.Line(); } else { code.Sub.Line(); }

			return true;
		}

		public virtual bool GenerateCodeForTypeMethod__CollectionWrapperBase_WrappItem( Type modelType, TypeSpecification collectionItemTypeSpecification, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == collectionItemTypeSpecification ) { throw new ArgumentNullException( "collectionItemTypeSpecification" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }
			/*
				Protected Overrides Function WrapItem(ByVal nativeItem As Object) As Common.IWrapper
					Dim rslt As New TcKs.MSOffice.Excel.Workbook(nativeItem, Me.RootObject)
					Return rslt
				End Function
			 */

			code.Protected.Overrides.Function._( "WrapItem" )._V.ByVal_( "nativeItem" )._As_<object>().V_.__.As_<TcKs.MSOffice.Common.IWrapper>().Line();
			using ( code.IndentBlock() ) {
				string strCollectionItemTypeFullName = this.GetTranslatedTypeFullName( collectionItemTypeSpecification.CollectionItemType );
				//code.Dim_( "rslt" )._As.New._( strCollectionItemTypeFullName )._V._( "nativeItem" ).i.Me.o_( "RootObject" ).V_.Line();
				bool isAppType = this.IsApplicationType( modelType );
				code.Dim_( "rslt" )._As.New._( strCollectionItemTypeFullName )._V._( "nativeItem" ).i_( isAppType ? " Me" : "Me.RootObject" ).V_.Line();
				code.Return_( "rslt" ).Line();
			}
			code.End.Function.Line();

			return true;
		}

		public virtual bool GenerateCodeForTypeMethod__CreateApplication( Type modelType, TypeSpecification typeSpecification, VbNetSourceCodeGenerator code ) {
			if ( null == modelType ) { throw new ArgumentNullException( "modelType" ); }
			if ( null == typeSpecification ) { throw new ArgumentNullException( "typeSpecification" ); }
			if ( null == code ) { throw new ArgumentNullException( "code" ); }
			/*
				Public Shared Function Create___() As ___
					Dim nativeObject As Object = CreateObject( "xxxxxxx" )
					Dim rslt As New ___( nativeObject )
					Return rslt
				End Function
			 */

			string strFullName = this.GetTranslatedTypeFullName( modelType );
			code.Public.Shared.Function._( "CreateApplication" )._VV_.As_( strFullName ).Line();
			using ( code.IndentBlock() ) {
				code.Dim_( "nativeObject" )._As_<object>()._E_( "CreateObject" )._V._( "\"CreateObject\"" ).V_.Line();
				code.Dim_( "rslt" )._As.New_( strFullName )._V._( "nativeObject" ).V_.Line();
				code.Return_( "rslt" ).Line();
			}
			code.End.Function.Line();

			return true;
		}

		protected virtual string GetBaseTypeFullName( Type source ) {
			if ( null == source ) { throw new ArgumentNullException( "source" ); }

			string ret = string.Empty;
			if ( this.Context.ApplicationTypes.Contains( source ) ) {
				ret = typeof( TcKs.MSOffice.Common.ApplicationWrapperBase ).ToString();
			}
			else if ( this.Context.TypeSpecifications.ContainsKey( source ) ) {
				TypeSpecification ts = this.Context.TypeSpecifications[source];
				if ( null != ts.ForcedBaseType ) {
					GeneratedTypeInfo gtiBaseType;
					this.Context.TypeMap.TryGetValue( ts.ForcedBaseType, out gtiBaseType );

					if ( null != gtiBaseType ) {
						ret = string.Format( "{0}.{1}", gtiBaseType.Namespace, gtiBaseType.Name );
					}
				}
				else if ( ts.IsCollection ) {
					GeneratedTypeInfo gtiItem = null;
					if ( null != ts.CollectionItemType ) {
						this.Context.TypeMap.TryGetValue( ts.CollectionItemType, out gtiItem );
					}

					if ( null == gtiItem ) {
						ret = typeof( TcKs.MSOffice.Common.CollectionWrapperBase ).FullName;
					}
					else {
						ret = string.Format( "{0}(Of {1}.{2})", typeof( TcKs.MSOffice.Common.CollectionWrapperBase ).FullName, gtiItem.Namespace, gtiItem.Name );
					}
				}
			}

			if ( string.IsNullOrEmpty( ret ) && null != this.Context.DefaultBaseType ) {
				ret = this.Context.DefaultBaseType.FullName;
			}

			ResolutionEventArgs<Type, string> args = new ResolutionEventArgs<Type, string>( source, ret );
			//string ret = this.OnBaseTypeFullNameResolution( source );
			this.OnBaseTypeFullNameResolution( args );

			return args.Result;
		}
		protected virtual string GetBaseTypeFullNameOrThrow( Type source ) {
			//string ret = this.OnBaseTypeFullNameResolution( source );
			string ret = this.GetBaseTypeFullName( source );
			if ( null != ret ) { ret = ret.Trim(); }
			if ( string.IsNullOrEmpty( ret ) ) {
				string message = string.Format( "The value '{0}' can not be used as base type name." );
				throw new InvalidOperationException( message );
			}

			return ret;
		}

		protected virtual string OnBaseTypeFullNameResolution( Type source ) {
			string ret = this.OnBaseTypeFullNameResolution( source, string.Empty );
			return ret;
		}
		protected virtual string OnBaseTypeFullNameResolution( Type source, string result ) {
			ResolutionEventArgs<Type, string> e = new ResolutionEventArgs<Type, string>( source, result );
			this.OnBaseTypeFullNameResolution( e );
			return e.Result;
		}
		protected virtual void OnBaseTypeFullNameResolution( ResolutionEventArgs<Type, string> e ) {
			EventHandler<ResolutionEventArgs<Type, string>> handler = this.BaseTypeFullNameResolution;
			if ( null != handler ) { handler( this, e ); }
		}
		public event EventHandler<ResolutionEventArgs<Type, string>> BaseTypeFullNameResolution;
	}

	/// <summary>
	/// Options for <typeparamref name="CodeGenerator"/> objects.
	/// </summary>
	public class CodeGeneratorOptions {
		private bool ignoreNotSupportedTypes;
		/// <summary>
		/// If TRUE the types which is not supported by code generator will be ignored.
		/// Otherwise code generator throws a exception.
		/// </summary>
		public bool IgnoreNotSupportedTypes {
			get { return this.ignoreNotSupportedTypes; }
			set { this.ignoreNotSupportedTypes = value; }
		}

		private bool ignoreNotSupportedMembers;
		/// <summary>
		/// If TRUE the members which is not supported by code generator will be ignored.
		/// Otherwise code generator throws a exception.
		/// </summary>
		public bool IgnoreNotSupportedMembers {
			get { return this.ignoreNotSupportedMembers; }
			set { this.ignoreNotSupportedMembers = value; }
		}

		private bool generateInterfaceAsClass;
		/// <summary>
		/// If true, the interfaces from source Interop assembly will be generated as class.
		/// </summary>
		public bool GenerateInterfaceAsClass {
			get { return this.generateInterfaceAsClass; }
			set { this.generateInterfaceAsClass = value; }
		}

		private bool ignorePropertiesWithParameters;
		/// <summary>
		/// If true, the properties with parameters will be ignored.
		/// </summary>
		public bool IgnorePropertiesWithParameters {
			get { return this.ignorePropertiesWithParameters; }
			set { this.ignorePropertiesWithParameters = value; }
		}

		private List<Type> applicationTypes = new List<Type>();
		/// <summary>
		/// Types which are "Application" (root object representing whole application) in sense of MS Office Interop object model.
		/// </summary>
		public List<Type> ApplicationTypes {
			get { return this.applicationTypes; }
		}

		private List<Type> ignoredTypes = new List<Type>();
		/// <summary>
		/// Types which will be ignored.
		/// </summary>
		public List<Type> IgnoredTypes {
			get { return this.ignoredTypes; }
		}

		private List<string> namesOfIgnoredTypeMembers = new List<string>();
		/// <summary>
		/// Names of members which will be ignored.
		/// </summary>
		public List<string> NamesOfIgnoredTypeMembers {
			get { return this.namesOfIgnoredTypeMembers; }
		}
	}

	/// <summary>
	/// Context data used by <typeparamref name="CodeGenerator"/> class.
	/// </summary>
	public class CodeGeneratorContext {
		private Dictionary<Type, GeneratedTypeInfo> typeMap = new Dictionary<Type, GeneratedTypeInfo>();
		/// <summary>
		/// Pairs with original type and additional informations about generated type.
		/// </summary>
		public Dictionary<Type, GeneratedTypeInfo> TypeMap {
			get { return this.typeMap; }
		}

		private Dictionary<Type, TypeSpecification> typeSpecifications = new Dictionary<Type, TypeSpecification>();
		/// <summary>
		/// Pairs with original type and additional specification about generated type.
		/// </summary>
		public Dictionary<Type, TypeSpecification> TypeSpecifications {
			get { return this.typeSpecifications; }
		}

		private Type defaultBaseType;
		/// <summary>
		/// Default base type used for generated classes.
		/// </summary>
		public Type DefaultBaseType {
			get { return this.defaultBaseType; }
			set { this.defaultBaseType = value; }
		}

		private List<Type> applicationTypes = new List<Type>();
		/// <summary>
		/// Types which are "Application" (root object representing whole application) in sense of MS Office Interop object model.
		/// </summary>
		public List<Type> ApplicationTypes {
			get { return this.applicationTypes; }
		}
	}

	/// <summary>
	/// Contains specification of target type.
	/// </summary>
	public class TypeSpecification {
		private bool isCollection;
		/// <summary>
		/// Determines if the type is a collection.
		/// </summary>
		public bool IsCollection {
			get { return this.isCollection; }
			set { this.isCollection = value; }
		}

		private Type collectionItemType;
		/// <summary>
		/// Determines which type is used for collection items.
		/// </summary>
		public Type CollectionItemType {
			get { return this.collectionItemType; }
			set { this.collectionItemType = value; }
		}

		public TypeSpecification() { }

		/// <summary>
		/// Creates a new <typeparamref name="TypeSpecification"/> instance for collection of specified item types.
		/// </summary>
		/// <typeparam name="T_COLLECTION_ITEM">Type of items in collection.</typeparam>
		/// <returns></returns>
		public static TypeSpecification SpecifyCollection<T_COLLECTION_ITEM>() {
			return SpecifyCollection( typeof( T_COLLECTION_ITEM ) );
		}
		/// <summary>
		/// Creates a new <typeparamref name="TypeSpecification"/> instance for collection of specified item types.
		/// </summary>
		/// <param name="collectionItemType">Type of items in collection.</param>
		/// <returns></returns>
		public static TypeSpecification SpecifyCollection( Type collectionItemType ) {
			TypeSpecification ts = new TypeSpecification();
			ts.CollectionItemType = collectionItemType;
			ts.IsCollection = true;

			return ts;
		}

		private List<MemberInfo> ignoredMembers = new List<MemberInfo>();
		public List<MemberInfo> IgnoredMembers {
			get { return this.ignoredMembers; }
		}

		private Type forcedBaseType;
		public Type ForcedBaseType {
			get { return this.forcedBaseType; }
			set { this.forcedBaseType = value; }
		}

		private bool isAbstractClass;
		public bool IsAbstractClass {
			get { return this.isAbstractClass; }
			set { this.isAbstractClass = value; }
		}

		private string applicationCOMName;
		public string ApplicationCOMName {
			get { return this.applicationCOMName; }
			set { this.applicationCOMName = value; }
		}
	}

	/// <summary>
	/// Contains information about original (wrapped) type and generated (wrapper) type.
	/// </summary>
	public class GeneratedTypeInfo {
		private Type modelType;
		/// <summary>
		/// Original (wrapped) type.
		/// </summary>
		public Type ModelType {
			get { return this.modelType; }
			set { this.modelType = value; }
		}

		private string @namespace;
		/// <summary>
		/// Namespace of generated (wrapper) type.
		/// </summary>
		public string Namespace {
			get { return this.@namespace; }
			set { this.@namespace = value; }
		}

		private string name;
		/// <summary>
		/// Name of generated (wrapper) type.
		/// </summary>
		public string Name {
			get { return this.name; }
			set { this.name = value; }
		}

		/// <summary>
		/// Returns full name of generated (wrapper) type.
		/// </summary>
		/// <returns></returns>
		public virtual string GetFullName() {
			string nmsp = this.Namespace;
			string name = this.Name;

			if ( string.IsNullOrEmpty( nmsp ) ) { return name; }
			else { return string.Format( "{0}.{1}", nmsp, name ); }
		}

		public GeneratedTypeInfo() { }
		public GeneratedTypeInfo( Type modelType ) {
			this.ModelType = modelType;
		}
		public GeneratedTypeInfo( Type modelType, string name ) : this( modelType ) {
			this.Name = name;
		}
		public GeneratedTypeInfo( Type modelType, string @namespace, string name ) : this( modelType, name ) {
			this.Namespace = @namespace;
		}
	}
}
